﻿namespace Jsl.FxCop
{
    using System.Linq;
    using System.Windows.Forms;
    using Microsoft.FxCop.Sdk;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class When_finding_the_WeakestClassUsed
    {
        [TestMethod]
        public void Null_if_nothing_used()
        {
            var variableUsage = new VariableUsage();

            AssertEx.That(() => variableUsage.WeakestClassUsed == null);
        }

        [TestMethod]
        public void Only_class_if_only_one_class_is_used()
        {
            var controlType = typeof(Control).GetTypeNode();

            var variableUsage = new VariableUsage();
            variableUsage.AddUsage(controlType);

            AssertEx.That(() => object.ReferenceEquals(variableUsage.WeakestClassUsed, controlType));
            AssertEx.That(() => !variableUsage.WeakestInterfacesUsed.Any());
        }

        [TestMethod]
        public void Using_the_same_class_multiple_times_is_OK()
        {
            var controlType = typeof(Control).GetTypeNode();

            var variableUsage = new VariableUsage();
            variableUsage.AddUsage(controlType);
            variableUsage.AddUsage(controlType);

            AssertEx.That(() => object.ReferenceEquals(variableUsage.WeakestClassUsed, controlType));
            AssertEx.That(() => !variableUsage.WeakestInterfacesUsed.Any());
        }

        [TestMethod]
        public void Weakest_of_multiple_classes_used()
        {
            var controlType = typeof(Control).GetTypeNode();
            var formType = typeof(Form).GetTypeNode();

            // Test both types being provided first.
            this.Weakest_of_multiple_classes_used(controlType, formType, controlType);
            this.Weakest_of_multiple_classes_used(formType, controlType, controlType);
        }

        private void Weakest_of_multiple_classes_used(
            TypeNode type1,
            TypeNode type2,
            TypeNode expectedType)
        {
            var variableUsage = new VariableUsage();
            variableUsage.AddUsage(type1);
            variableUsage.AddUsage(type2);

            AssertEx.That(() => object.ReferenceEquals(variableUsage.WeakestClassUsed, expectedType));
            AssertEx.That(() => !variableUsage.WeakestInterfacesUsed.Any());
        }
    }

    [TestClass]
    public class When_finding_the_WeakestInterfacesUsed
    {
        [TestMethod]
        public void Empty_if_nothing_used()
        {
            var variableUsage = new VariableUsage();

            AssertEx.That(() => !variableUsage.WeakestInterfacesUsed.Any());
        }

        [TestMethod]
        public void Only_interface_if_only_one_interface_is_used()
        {
            var mostDerivedType = typeof(IMostDerived).GetTypeNode();

            var variableUsage = new VariableUsage();
            variableUsage.AddUsage(mostDerivedType);

            AssertEx.That(() => variableUsage.WeakestInterfacesUsed.Count() == 1);
            AssertEx.That(() => object.ReferenceEquals(variableUsage.WeakestInterfacesUsed.ElementAt(0), mostDerivedType));
        }

        [TestMethod]
        public void Using_the_same_interface_mutiple_times_is_OK()
        {
            var mostDerivedType = typeof(IMostDerived).GetTypeNode();

            var variableUsage = new VariableUsage();
            variableUsage.AddUsage(mostDerivedType);
            variableUsage.AddUsage(mostDerivedType);

            AssertEx.That(() => variableUsage.WeakestInterfacesUsed.Count() == 1);
            AssertEx.That(() => object.ReferenceEquals(variableUsage.WeakestInterfacesUsed.ElementAt(0), mostDerivedType));
        }

        [TestMethod]
        public void Weakest_interface_if_in_chain_of_inheritance()
        {
            var mostDerivedType = typeof(IMostDerived).GetTypeNode();
            var derived2Type = typeof(IDerived2).GetTypeNode();

            var variableUsage = new VariableUsage();
            variableUsage.AddUsage(mostDerivedType);
            variableUsage.AddUsage(derived2Type);

            AssertEx.That(() => variableUsage.WeakestInterfacesUsed.Count() == 1);
            AssertEx.That(() => object.ReferenceEquals(variableUsage.WeakestInterfacesUsed.ElementAt(0), derived2Type));
        }

        [TestMethod]
        public void Weakest_interface_if_multiple_inheritance_chains()
        {
            var baseType = typeof(IBase).GetTypeNode();
            var derived1Type = typeof(IDerived1).GetTypeNode();
            var derived2Type = typeof(IDerived2).GetTypeNode();

            var variableUsage = new VariableUsage();
            variableUsage.AddUsage(derived1Type);
            variableUsage.AddUsage(derived2Type);

            AssertEx.That(() => variableUsage.WeakestInterfacesUsed.Count() == 1);
            AssertEx.That(() => object.ReferenceEquals(variableUsage.WeakestInterfacesUsed.ElementAt(0), baseType));
        }
    }

    internal interface IBase
    {
    }

    internal interface IDerived1 : IBase
    {
    }

    internal interface IDerived2 : IBase
    {
    }

    internal interface IMostDerived : IDerived1, IDerived2
    {
    }
}